import org.antlr.v4.runtime.ANTLRInputStream;
import org.antlr.v4.runtime.CommonTokenStream;

import java.io.*;
import java.util.ArrayList;
import java.util.Random;
import java.util.regex.Pattern;

public class GenticProgram
{

    public static boolean isInteger(String str) {
        if (null == str || "".equals(str)) {
            return false;
        }
        Pattern pattern = Pattern.compile("^[-\\+]?[\\d]*$");
        return pattern.matcher(str).matches();
    }


    public static boolean isDouble(String str) {
        if (null == str || "".equals(str)) {
            return false;
        }
        Pattern pattern = Pattern.compile("^[-\\+]?[.\\d]*$");
        return pattern.matcher(str).matches();
    }

    public static Boolean randomBoolean(double frac)
    {
        double r=Math.random();
        if(r<frac)
        {
            return true;
        }
        else{
            return false;
        }
    }
    public static int randomInt(int min,int max)
    {
        Random random = new Random();
        return random.nextInt(max)%(max-min+1) + min;
    }
    public static double randomDouble(int min,int max)
    {
        double r=Math.random();
        Random random = new Random();
        int randomInt = random.nextInt(max)%(max-min+1) + min;
        return randomInt+r;
    }
    public static boolean randomChangeParamValue(Tree tree,ArrayList<Gene> geneList)
    {
        boolean ret=false;
        if(!tree.isEmpty()) {
            if (tree.getRootData().equals("literal") && tree.getChildCount() ==1)
            {
                if (tree.getChild(0).getRootData().equals("booleanliteral") && tree.getChildCount() ==1)
                {
                    Tree paramValue=tree.getChild(0).getChild(0);
                    System.out.print("Bool:"+paramValue.getRootData());
                    System.out.println();
                    Boolean randBool=randomBoolean(0.5);
                    tree.getChild(0).getChild(0).setRootData(randBool);

                    Gene gene=new Gene("Boolean",randBool);
                    geneList.add(gene);

                    ret=true;
                    return ret;

                }

                else {
                    Tree paramValue = tree.getChild(0);
                    String text=paramValue.getRootData().toString();
                    if(isInteger(text))
                    {
                        System.out.print("Int:"+text);
                        System.out.println();
                        int randInt=randomInt(0,10000);
                        tree.getChild(0).setRootData(randInt);

                        Gene gene=new Gene("Int",randInt);
                        geneList.add(gene);

                        ret=true;
                        return ret;
                    }
                    else if(isDouble(text))
                    {
                        System.out.print("Double:"+text);
                        System.out.println();
                        double randDouble=randomDouble(0,5);
                        tree.getChild(0).setRootData(randDouble);

                        Gene gene=new Gene("Double",randDouble);
                        geneList.add(gene);

                        ret=true;
                        return ret;
                    }
                    else {
                        System.out.print("String:"+text);
                        System.out.println();

                        Gene gene=new Gene("String",text);
                        geneList.add(gene);

                        ret=true;
                        return ret;
                    }

                }
            }
            else if(tree.getRootData().equals("idexpression") && tree.getChildCount() ==1)
            {
                if (tree.getChild(0).getRootData().equals("unqualifiedid") && tree.getChildCount() ==1)
                {
                    Tree paramValue = tree.getChild(0).getChild(0);
                    System.out.print("Variable:"+paramValue.getRootData());
                    System.out.println();

                    Gene gene=new Gene("Variable",paramValue.getRootData().toString());
                    geneList.add(gene);

                    ret=true;
                    return ret;
                }
            }

            if (tree.getChildCount() > 0) {

                for (int i = 0; i < tree.getChildCount(); i++) {
                    boolean iterRet=randomChangeParamValue(tree.getChild(i),geneList);
                    if(!ret)
                    {
                        ret=iterRet;
                    }
                }
            }
        }
        return ret;
    }


    public static void findParamValue(Tree tree,ArrayList<Gene> geneList)
    {
        if(!tree.isEmpty()) {
            if (tree.getRootData().equals("literal") && tree.getChildCount() ==1)
            {
                if (tree.getChild(0).getRootData().equals("booleanliteral") && tree.getChildCount() ==1)
                {
                    Tree paramValue=tree.getChild(0).getChild(0);
                    System.out.print("Bool:"+paramValue.getRootData());
                    System.out.println();

                }

                else {
                    Tree paramValue = tree.getChild(0);
                    String text=paramValue.getRootData().toString();
                    if(isInteger(text))
                    {
                        System.out.print("Int:"+text);
                        System.out.println();
                    }
                    else if(isDouble(text))
                    {
                        System.out.print("Double:"+text);
                        System.out.println();
                    }
                    else {
                        System.out.print("String:"+text);
                        System.out.println();
                    }

                }
            }
            else if(tree.getRootData().equals("idexpression") && tree.getChildCount() ==1)
            {
                if (tree.getChild(0).getRootData().equals("unqualifiedid") && tree.getChildCount() ==1)
                {
                    Tree paramValue = tree.getChild(0).getChild(0);
                    System.out.print("Variable:"+paramValue.getRootData());
                    System.out.println();
                }
            }

            if (tree.getChildCount() > 0) {

                for (int i = 0; i < tree.getChildCount(); i++) {
                    findParamValue(tree.getChild(i),geneList);
                }
            }
        }
    }
    public static boolean randomChangeParamDecl(Tree tree,ArrayList<Gene> geneList)
    {
        boolean ret=false;
        TreeProcessor tp=new TreeProcessor();
        if(!tree.isEmpty()) {
            if (tree.getRootData().equals("postfixexpression") && tree.getChildCount() ==4)
            {
                Tree paramDecl=tree.getChild(0);

                StringBuffer sb=new StringBuffer();

                if(paramDecl.getChildCount()==3 )
                {
                    tp.getLeafText(paramDecl.getChild(2),sb);
                    //System.out.println(sb.toString());
                    if(sb.toString().strip().equals("param")){
                        Tree expressionlist = tree.getChild(2);
                        Tree paramValue=expressionlist.getChild(0).getChild(2);
                        Tree paramName=expressionlist.getChild(0).getChild(0).getChild(0);
                        StringBuffer sb2=new StringBuffer();
                        tp.getLeafText(paramName,sb2);
                        System.out.print(sb2.toString()+"\t");
                        //System.out.print(tree.getRootData());
                        ret=randomChangeParamValue(tree.getChild(2).getChild(0).getChild(2),geneList);
                        return ret;
                    }
                }
            }

            if (tree.getChildCount() > 0) {

                for (int i = 0; i < tree.getChildCount(); i++) {
                    boolean iterRet=randomChangeParamDecl(tree.getChild(i),geneList);
                    if(!ret)
                    {
                        ret=iterRet;
                    }
                }
            }
        }
        //System.out.println(ret);
        return ret;
    }
    public static void findParamDecl(Tree tree,ArrayList<Gene> geneList,ArrayList<String> paramNameList)
    {
        TreeProcessor tp=new TreeProcessor();
        if(!tree.isEmpty()) {
            if (tree.getRootData().equals("postfixexpression") && tree.getChildCount() ==4)
            {
                Tree paramDecl=tree.getChild(0);

                StringBuffer sb=new StringBuffer();

                if(paramDecl.getChildCount()==3 )
                {
                    tp.getLeafText(paramDecl.getChild(2),sb);
                    //System.out.println(sb.toString());
                    if(sb.toString().strip().equals("param")){
                        Tree expressionlist = tree.getChild(2);
                        Tree paramValue=expressionlist.getChild(0).getChild(2);
                        Tree paramName=expressionlist.getChild(0).getChild(0).getChild(0);
                        StringBuffer sb2=new StringBuffer();
                        tp.getLeafText(paramName,sb2);
                        System.out.print(sb2.toString()+"\t");
                        String name=sb2.toString().replace("\"","");
                        name=name.strip();
                        paramNameList.add(name);
                        //System.out.print(tree.getRootData());
                        findParamValue(paramValue,geneList);

                    }
                }
            }

            if (tree.getChildCount() > 0) {

                for (int i = 0; i < tree.getChildCount(); i++) {
                    findParamDecl(tree.getChild(i),geneList,paramNameList);
                }
            }
        }

    }


    public static  String readFromTextFile(String pathname) throws IOException {
        String code="";
        File filename = new File(pathname);
        InputStreamReader reader = new InputStreamReader(new FileInputStream(filename));
        BufferedReader br = new BufferedReader(reader);
        String line = "";
        line = br.readLine();
        while(line != null) {
            code+=line+"\n";
            line = br.readLine();
        }
        return code;
    }


    public static void genCode(Tree myTree,ArrayList<ArrayList<Gene>> populationGeneList,ArrayList<String> paramNameList) throws IOException {
        randomChangeFunc(myTree,populationGeneList,10,paramNameList);

    }
    public static void genAADL(int index) throws IOException {
        PrintStream psOld = System.out;
        String baseDir="H:\\aadl_lib\\";
        String genDir="H:\\AADL_Gen\\";

        //String path=genDir+"planner_core_initialize_re"+index+".aadl";
        String path=genDir+"planner_core"+index+".aadl";
        String basePath=baseDir+"planner_core.aadl";
        File f = new File(path);

        if(!f.exists()){
            f.createNewFile();
        }
        String code=readFromTextFile(basePath);
        System.out.println(code);
        System.setOut(new PrintStream(f));
        System.out.println(code);
        AADL generator=new AADL();

        generator.genAADLSys(index,"planner_core");
        generator.genAADLEnd();

        System.setOut(psOld);

    }

    public static void randomChangeFunc(Tree myTtree,ArrayList<ArrayList<Gene>> populationGeneList,int pn,ArrayList<String> paramNameList) throws IOException {
        if(!myTtree.isEmpty()) {
            if(myTtree.getRootData().equals("functionbody")&&myTtree.getChildCount()==1){//find call function,make it return value
                TreeProcessor tp=new TreeProcessor();
                ArrayList<Gene> geneList=new ArrayList<>();
                Tree funcTree=myTtree;
                boolean changed=randomChangeParamDecl(funcTree,geneList);
                if(changed)
                {
                    populationGeneList.add(geneList);

                    System.out.println("\nPopulation 1 individual 0:");
                    findParamDecl(funcTree,geneList,paramNameList);
                    //tp.printCode(myTree);

                    for(int i=0;i<pn;i++)
                    {
                        ArrayList<Gene> geneList_iter=new ArrayList<>();
                        Tree myTree_iter=myTtree;


                        //findParamDecl(myTree);
                        System.out.println();
                        randomChangeParamDecl(myTree_iter,geneList_iter);
                        System.out.println("\nPopulation 1 individual "+(i+1)+":");
                        findParamDecl(myTree_iter,geneList_iter,paramNameList);
                        populationGeneList.add(geneList_iter);


                        PrintStream psOld = System.out;
                        String genDir="H:\\aadl_lib\\";
                        String path=genDir+"planner_core_initialize_re_"+i+".cpp";
                        File f = new File(path);
                        if(!f.exists()){
                            f.createNewFile();
                        }
                        System.setOut(new PrintStream(new File(path)));
                        tp.printCode(myTree_iter);
                        System.setOut(psOld);

                        //genAADL(i);

                    }

                }

            }
            for(Tree child : myTtree.getChilds()) {
                if(child != null) {
                    randomChangeFunc(child,populationGeneList,pn,paramNameList);
                }
            }
        }
    }
    public static void getRosParamFromCpp(String loadPath,ArrayList<ArrayList<Gene>> populationGeneList,int pn,ArrayList<String> paramNameList) throws IOException {
        TreeProcessor tp=new TreeProcessor();
        String code=readFromTextFile(loadPath);

        ANTLRInputStream input = new ANTLRInputStream(code);
        CPP14Lexer lexer = new CPP14Lexer(input);
        CommonTokenStream tokens = new CommonTokenStream(lexer);
        CPP14Parser parser = new CPP14Parser(tokens);
        CPP14Parser.TranslationunitContext tree = parser.translationunit();

        tree.removeLastChild();
        Tree myTree=new Tree(tp.getRuleName(tree,parser));
        tp.convertTree(tree,parser,myTree);

        //findParamDecl(myTree);
        genCode(myTree,populationGeneList,paramNameList);
        /*ArrayList<Gene> geneList=new ArrayList<>();
        boolean changed=randomChangeParamDecl(myTree,geneList);
        if(changed)
        {
            populationGeneList.add(geneList);

            System.out.println("\nPopulation 1 individual 0:");
            findParamDecl(myTree,geneList);
            //tp.printCode(myTree);

            for(int i=0;i<pn;i++)
            {
                ArrayList<Gene> geneList_iter=new ArrayList<>();
                Tree myTree_iter=new Tree(tp.getRuleName(tree,parser));
                tp.convertTree(tree,parser,myTree_iter);

                //findParamDecl(myTree);
                System.out.println();
                randomChangeParamDecl(myTree_iter,geneList_iter);
                System.out.println("\nPopulation 1 individual "+(i+1)+":");
                findParamDecl(myTree_iter,geneList_iter);
                populationGeneList.add(geneList_iter);
                tp.printCode(myTree_iter);
            }

        }*/

    }

    public static void getRosParamFromDir(String dirPath,ArrayList<ArrayList<Gene>> populationGeneList,int pn,ArrayList<String> paramNameList) throws IOException {
        File f=new File(dirPath);
        if(f.isDirectory()) {
            File src = new File(f.toString() + "\\src");
            File[] srcs = src.listFiles();
            for (File s : srcs) {
                if (s.getPath().endsWith(".cpp")) {
                    System.out.println("File:"+s.getPath());
                    getRosParamFromCpp(s.getPath(),populationGeneList,pn,paramNameList);
                }
            }
        }
    }


    public static void exchangeGene(ArrayList<Gene> geneList1,ArrayList<Gene> geneList2,int index)
    {
        //System.out.println("gene cross:");
        //System.out.print("type:\t");
        switch (geneList1.get(index).getType())
        {
            case "Boolean":
                System.out.println("Boolean\t"+geneList1.get(index).getBooleanValue()+" <--> "+geneList2.get(index).getBooleanValue());
                Gene temp=geneList1.get(index);
                geneList1.get(index).setBooleanValue(geneList2.get(index).getBooleanValue());
                geneList2.get(index).setBooleanValue(temp.getBooleanValue());
                break;
            case "Int":
                System.out.println("Int\t"+geneList1.get(index).getIntValue()+" <--> "+geneList2.get(index).getIntValue());
                Gene temp2=geneList1.get(index);
                geneList1.get(index).setIntValue(geneList2.get(index).getIntValue());
                geneList2.get(index).setIntValue(temp2.getIntValue());
                break;
            case "Double":
                System.out.println("Double\t"+geneList1.get(index).getDoubleValue()+" <--> "+geneList2.get(index).getDoubleValue());
                Gene temp3=geneList1.get(index);
                geneList1.get(index).setDoubleValue(geneList2.get(index).getDoubleValue());
                geneList2.get(index).setDoubleValue(temp3.getDoubleValue());
                break;
            case "String":
                System.out.println("String\t"+geneList1.get(index).getStringValue()+" <--> "+geneList2.get(index).getStringValue());
                Gene temp4=geneList1.get(index);
                geneList1.get(index).setStringValue(geneList2.get(index).getStringValue());
                geneList2.get(index).setStringValue(temp4.getStringValue());
                break;
            case "Variable":
                System.out.println("Variable\t"+geneList1.get(index).getStringValue()+" <--> "+geneList2.get(index).getStringValue());
                Gene temp5=geneList1.get(index);
                geneList1.get(index).setStringValue(geneList2.get(index).getStringValue());
                geneList2.get(index).setStringValue(temp5.getStringValue());
                break;
        }
        //System.out.println();
    }
    public static void printGene(ArrayList<Gene> geneList1,ArrayList<Gene> geneList2,int index)
    {
        switch (geneList1.get(index).getType())
        {
            case "Boolean":
                System.out.println("Boolean\t"+geneList1.get(index).getBooleanValue()+"     "+geneList2.get(index).getBooleanValue());
                break;
            case "Int":
                System.out.println("Int\t"+geneList1.get(index).getIntValue()+"     "+geneList2.get(index).getIntValue());
                break;
            case "Double":
                System.out.println("Double\t"+geneList1.get(index).getDoubleValue()+"     "+geneList2.get(index).getDoubleValue());
                break;
            case "String":
                System.out.println("String\t"+geneList1.get(index).getStringValue()+"     "+geneList2.get(index).getStringValue());
                break;
            case "Variable":
                System.out.println("Variable\t"+geneList1.get(index).getStringValue()+"     "+geneList2.get(index).getStringValue());
                break;
        }
    }
    public static void cross(ArrayList<ArrayList<Gene>> populationGeneList,double crossFrac,int iter,ArrayList<Boolean> paramChoose)
    {
        for(int it=0;it<iter;it++) {

            int i1 = randomInt(0, populationGeneList.size());
            int i2 = randomInt(0, populationGeneList.size());
            while (i1 == i2) {
                i2 = randomInt(0, populationGeneList.size());
            }
            ArrayList<Gene> geneList1 = populationGeneList.get(i1);
            ArrayList<Gene> geneList2 = populationGeneList.get(i2);
            System.out.println("***** cross iter "+it+" *****\n"+i1+" <--> "+i2);
            for (int i = 0; i < geneList1.size(); i++) {
                double rd = randomDouble(0, 1);
                //if (rd < crossFrac) {
                if(paramChoose.get(i)==true)
                {
                    exchangeGene(populationGeneList.get(i1), populationGeneList.get(i2), i);
                }
                else
                {
                    printGene(populationGeneList.get(i1), populationGeneList.get(i2), i);
                }
            }
            System.out.println();


        }

    }
    public static void printYaml(ArrayList<Gene> geneList,ArrayList<String> paramNameList)
    {
        for (int i = 0; i < geneList.size(); i++) {
            switch (geneList.get(i).getType()) {
                case "Boolean":
                    System.out.println("  "+paramNameList.get(i)+": " + geneList.get(i).getBooleanValue());
                    break;
                case "Int":
                    System.out.println("  "+paramNameList.get(i)+": " + geneList.get(i).getIntValue());
                    break;
                case "Double":
                    System.out.println("  "+paramNameList.get(i)+": " + geneList.get(i).getDoubleValue());
                    break;
                case "String":
                    System.out.println("  "+paramNameList.get(i)+": " + geneList.get(i).getStringValue());
                    break;
                case "Variable":
                    System.out.println("  "+paramNameList.get(i)+": " + geneList.get(i).getStringValue());
                    break;
            }
        }
    }
    public static void printToYaml(ArrayList<ArrayList<Gene>> populationGeneList,String rootPath,ArrayList<String> paramNameList) throws IOException {

        for(int it=0;it<populationGeneList.size();it++) {
            String path = rootPath+"\\"+it+".yaml";
            File f =new File(path);
            if(!f.exists())
            {
                f.createNewFile();
            }
            ArrayList<Gene> geneList = populationGeneList.get(it);

            System.out.println("write to yaml");
            PrintStream psOld = System.out;
            System.setOut(new PrintStream(new File(path)));
            System.out.println("GlobalPlanner:");
            printYaml(geneList,paramNameList);
            System.setOut(psOld);

            System.out.println();


        }
    }

    public static void genPopulation(int pn) throws IOException
    {
        ArrayList<ArrayList<Gene>> populationGeneList=new ArrayList<>();
        ArrayList<String> paramNameList=new ArrayList<>();
        getRosParamFromDir("E:\\u\\home\\catkin_ws\\src\\navigation\\global_planner", populationGeneList,pn,paramNameList);
        //getRosParamFromCpp("E:\\gen_test\\global_planner_initialize_re.cpp",populationGeneList,pn);
        System.out.println(populationGeneList.size());
        printToYaml(populationGeneList,"H:\\yaml_gen",paramNameList);


        ArrayList<Boolean> paramChoose=new ArrayList<>();
        System.out.println("exchange or not");
        for(int i=0;i<populationGeneList.get(0).size();i++)
        {
            paramChoose.add(randomBoolean(0.5));
            System.out.println(paramChoose.get(i));
        }
        cross(populationGeneList,0.5,10,paramChoose);
    }
    public static void genNextPopulation(ArrayList<ArrayList<Gene>> populationGeneList) throws IOException
    {
        int pn=10;
        ArrayList<String> paramNameList=new ArrayList<>();
        getRosParamFromDir("E:\\u\\home\\catkin_ws\\src\\navigation\\global_planner", populationGeneList,pn,paramNameList);
        //getRosParamFromCpp("E:\\gen_test\\global_planner_initialize_re.cpp",populationGeneList,pn);
        System.out.println(populationGeneList.size());
        printToYaml(populationGeneList,"H:\\yaml_gen",paramNameList);


        ArrayList<Boolean> paramChoose=new ArrayList<>();
        System.out.println("exchange or not");
        for(int i=0;i<populationGeneList.get(0).size();i++)
        {
            paramChoose.add(randomBoolean(0.5));
            System.out.println(paramChoose.get(i));
        }
        cross(populationGeneList,0.5,10,paramChoose);
    }

    public static void main(String[] args) throws IOException {
        //String loadPath="E:\\u\\home\\catkin_ws\\src\\navigation\\global_planner\\src\\planner_core.cpp";
        //getRosParamFromCpp(loadPath);
        //getRosParamFromDir("E:\\u\\home\\catkin_ws\\src\\navigation\\move_base");
        genPopulation(10);
        //genNextPopulation(10);

    }
}
